<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>无人机活塞 - 3D 模型查看器</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            width: 100%;
            height: 100vh;
            overflow: hidden;
            background: linear-gradient(135deg, #0a1929 0%, #1a237e 50%, #283593 100%);
            background-attachment: fixed;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            position: relative;
        }

        body::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-image: 
                linear-gradient(rgba(33, 150, 243, 0.1) 1px, transparent 1px),
                linear-gradient(90deg, rgba(33, 150, 243, 0.1) 1px, transparent 1px);
            background-size: 50px 50px;
            pointer-events: none;
            animation: gridMove 20s linear infinite;
            z-index: 0;
        }

        @keyframes gridMove {
            0% { transform: translate(0, 0); }
            100% { transform: translate(50px, 50px); }
        }

        body::after {
            content: '';
            position: absolute;
            top: -50%;
            left: -50%;
            width: 200%;
            height: 200%;
            background: radial-gradient(circle, rgba(33, 150, 243, 0.15) 0%, transparent 70%);
            animation: rotate 20s linear infinite;
            pointer-events: none;
            z-index: 0;
        }

        @keyframes rotate {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        #canvas-container {
            width: 100%;
            height: 100%;
            position: relative;
            z-index: 1;
        }

        #loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: white;
            font-size: 18px;
            z-index: 100;
            text-align: center;
        }

        .loading-spinner {
            border: 4px solid rgba(255, 255, 255, 0.3);
            border-top: 4px solid white;
            border-radius: 50%;
            width: 40px;
            height: 40px;
            animation: spin 1s linear infinite;
            margin: 0 auto 20px;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        #controls {
            position: absolute;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0, 0, 0, 0.5);
            padding: 15px 25px;
            border-radius: 25px;
            color: white;
            font-size: 14px;
            z-index: 10;
            backdrop-filter: blur(10px);
        }

        #controls p {
            margin: 5px 0;
        }

        .hidden {
            display: none;
        }

        #info {
            position: absolute;
            top: 20px;
            left: 20px;
            background: rgba(0, 0, 0, 0.5);
            padding: 15px;
            border-radius: 10px;
            color: white;
            font-size: 14px;
            z-index: 10;
            backdrop-filter: blur(10px);
            max-width: 300px;
        }

        /* 标题区域 */
        #title-section {
            position: absolute;
            top: 40px;
            left: 40px;
            z-index: 20;
            color: white;
        }

        #title-section .icon {
            display: inline-block;
            width: 24px;
            height: 24px;
            margin-right: 10px;
            vertical-align: middle;
        }

        #title-section h1 {
            font-size: 28px;
            font-weight: bold;
            margin-bottom: 5px;
            display: inline-block;
        }

        #title-section .subtitle {
            font-size: 14px;
            color: rgba(255, 255, 255, 0.8);
            margin-top: 5px;
        }

        /* 右侧说明面板 */
        #description-panel {
            position: absolute;
            top: 50%;
            right: 40px;
            transform: translateY(-50%);
            width: 400px;
            min-height: 300px;
            background: rgba(30, 58, 138, 0.9);
            border: 2px solid rgba(59, 130, 246, 0.5);
            border-radius: 12px;
            padding: 30px;
            color: white;
            z-index: 20;
            backdrop-filter: blur(10px);
            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
            display: none;
        }

        #description-panel.show {
            display: block;
            animation: fadeIn 0.3s ease-in;
        }

        @keyframes fadeIn {
            from {
                opacity: 0;
                transform: translateY(-50%) translateX(20px);
            }
            to {
                opacity: 1;
                transform: translateY(-50%) translateX(0);
            }
        }

        #description-panel h2 {
            font-size: 20px;
            margin-bottom: 15px;
            color: #93c5fd;
        }

        #description-panel p {
            font-size: 16px;
            line-height: 1.8;
            color: rgba(255, 255, 255, 0.9);
        }

        /* 连接线canvas */
        #connection-line {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            z-index: 15;
        }

        /* 导航按钮区域 */
        #navigation-bar {
            position: absolute;
            bottom: 40px;
            left: 50%;
            transform: translateX(-50%);
            display: flex;
            gap: 15px;
            align-items: center;
            z-index: 20;
            background: linear-gradient(135deg, rgba(25, 118, 210, 0.15) 0%, rgba(13, 71, 161, 0.25) 100%);
            padding: 15px 30px;
            border-radius: 30px;
            backdrop-filter: blur(20px);
            border: 2px solid transparent;
            box-shadow: 
                0 8px 32px rgba(0, 0, 0, 0.3),
                0 0 0 1px rgba(33, 150, 243, 0.3) inset,
                0 0 40px rgba(33, 150, 243, 0.1);
            position: relative;
            overflow: visible;
        }

        #navigation-bar::before {
            content: '';
            position: absolute;
            top: -50%;
            left: -50%;
            width: 200%;
            height: 200%;
            background: linear-gradient(45deg, transparent, rgba(33, 150, 243, 0.3), transparent);
            transform: rotate(45deg);
            animation: shine 3s ease-in-out infinite;
            opacity: 0;
        }

        @keyframes shine {
            0%, 100% { opacity: 0; transform: translateX(-100%) translateY(-100%) rotate(45deg); }
            50% { opacity: 1; transform: translateX(100%) translateY(100%) rotate(45deg); }
        }

        .nav-button {
            padding: 12px 24px;
            background: linear-gradient(135deg, rgba(25, 118, 210, 0.2) 0%, rgba(13, 71, 161, 0.3) 100%);
            border: 2px solid rgba(33, 150, 243, 0.4);
            border-radius: 20px;
            color: #ffffff;
            cursor: pointer;
            font-size: 14px;
            font-weight: 600;
            transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            white-space: nowrap;
            position: relative;
            overflow: hidden;
            text-shadow: 0 0 10px rgba(33, 150, 243, 0.8), 0 0 20px rgba(33, 150, 243, 0.5);
            box-shadow: 
                0 4px 15px rgba(0, 0, 0, 0.2),
                0 0 0 1px rgba(33, 150, 243, 0.2) inset,
                0 0 20px rgba(33, 150, 243, 0.1);
        }

        .nav-button::before {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            width: 0;
            height: 0;
            border-radius: 50%;
            background: radial-gradient(circle, rgba(33, 150, 243, 0.8) 0%, transparent 70%);
            transform: translate(-50%, -50%);
            transition: width 0.6s ease, height 0.6s ease;
            z-index: 0;
        }

        .nav-button::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            border-radius: 20px;
            padding: 2px;
            background: linear-gradient(135deg, 
                rgba(33, 150, 243, 0.8) 0%,
                rgba(144, 202, 249, 0.6) 50%,
                rgba(33, 150, 243, 0.8) 100%);
            -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
            -webkit-mask-composite: xor;
            mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
            mask-composite: exclude;
            opacity: 0;
            transition: opacity 0.4s;
            z-index: -1;
        }

        .nav-button > * {
            position: relative;
            z-index: 1;
        }

        .nav-button:hover {
            background: linear-gradient(135deg, rgba(25, 118, 210, 0.3) 0%, rgba(13, 71, 161, 0.4) 100%);
            transform: translateY(-5px) scale(1.05);
            border-color: rgba(33, 150, 243, 0.8);
            box-shadow: 
                0 12px 30px rgba(33, 150, 243, 0.4),
                0 0 0 1px rgba(33, 150, 243, 0.6) inset,
                0 0 40px rgba(33, 150, 243, 0.3),
                0 0 60px rgba(33, 150, 243, 0.2);
            text-shadow: 
                0 0 15px rgba(33, 150, 243, 1),
                0 0 25px rgba(33, 150, 243, 0.8),
                0 0 35px rgba(33, 150, 243, 0.6);
        }

        .nav-button:hover::before {
            width: 300px;
            height: 300px;
        }

        .nav-button:hover::after {
            opacity: 1;
        }

        .nav-button:active {
            transform: translateY(-3px) scale(1.02);
            box-shadow: 
                0 8px 20px rgba(33, 150, 243, 0.5),
                0 0 0 2px rgba(33, 150, 243, 0.8) inset,
                0 0 50px rgba(33, 150, 243, 0.4);
        }

        .nav-button.active {
            background: linear-gradient(135deg, rgba(33, 150, 243, 0.4) 0%, rgba(25, 118, 210, 0.5) 100%);
            border-color: rgba(33, 150, 243, 1);
            box-shadow: 
                0 0 30px rgba(33, 150, 243, 0.8),
                0 0 50px rgba(33, 150, 243, 0.5),
                0 0 70px rgba(33, 150, 243, 0.3),
                0 0 0 2px rgba(33, 150, 243, 0.8) inset;
            text-shadow: 
                0 0 20px rgba(33, 150, 243, 1),
                0 0 30px rgba(33, 150, 243, 0.9),
                0 0 40px rgba(33, 150, 243, 0.7);
            animation: pulseGlow 2s ease-in-out infinite;
        }

        @keyframes pulseGlow {
            0%, 100% {
                box-shadow: 
                    0 0 30px rgba(33, 150, 243, 0.8),
                    0 0 50px rgba(33, 150, 243, 0.5),
                    0 0 70px rgba(33, 150, 243, 0.3),
                    0 0 0 2px rgba(33, 150, 243, 0.8) inset;
            }
            50% {
                box-shadow: 
                    0 0 40px rgba(33, 150, 243, 1),
                    0 0 60px rgba(33, 150, 243, 0.7),
                    0 0 80px rgba(33, 150, 243, 0.5),
                    0 0 0 3px rgba(33, 150, 243, 1) inset;
            }
        }

        .nav-arrow {
            padding: 10px 14px;
            background: linear-gradient(135deg, rgba(25, 118, 210, 0.2) 0%, rgba(13, 71, 161, 0.3) 100%);
            border: 2px solid rgba(33, 150, 243, 0.4);
            border-radius: 50%;
            color: #ffffff;
            cursor: pointer;
            font-size: 16px;
            transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            position: relative;
            overflow: hidden;
            text-shadow: 0 0 10px rgba(33, 150, 243, 0.8);
            box-shadow: 
                0 4px 15px rgba(0, 0, 0, 0.2),
                0 0 0 1px rgba(33, 150, 243, 0.2) inset,
                0 0 20px rgba(33, 150, 243, 0.1);
        }

        .nav-arrow::before {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            width: 0;
            height: 0;
            border-radius: 50%;
            background: radial-gradient(circle, rgba(33, 150, 243, 0.8) 0%, transparent 70%);
            transform: translate(-50%, -50%);
            transition: width 0.6s ease, height 0.6s ease;
            z-index: 0;
        }

        .nav-arrow > * {
            position: relative;
            z-index: 1;
        }

        .nav-arrow:hover {
            background: linear-gradient(135deg, rgba(25, 118, 210, 0.3) 0%, rgba(13, 71, 161, 0.4) 100%);
            transform: translateY(-5px) scale(1.1) rotate(5deg);
            border-color: rgba(33, 150, 243, 0.8);
            box-shadow: 
                0 12px 30px rgba(33, 150, 243, 0.4),
                0 0 0 1px rgba(33, 150, 243, 0.6) inset,
                0 0 40px rgba(33, 150, 243, 0.3);
            text-shadow: 
                0 0 15px rgba(33, 150, 243, 1),
                0 0 25px rgba(33, 150, 243, 0.8);
        }

        .nav-arrow:hover::before {
            width: 200px;
            height: 200px;
        }

        .nav-arrow:active {
            transform: translateY(-3px) scale(1.05) rotate(2deg);
        }

        /* 高亮点 */
        .highlight-point {
            position: absolute;
            width: 16px;
            height: 16px;
            background: #63b3ed; /* 浅蓝色，更符合科技感 */
            border: 2px solid rgba(255, 255, 255, 0.9);
            border-radius: 50%;
            box-shadow: 
                0 0 20px rgba(99, 179, 237, 1),
                0 0 40px rgba(99, 179, 237, 0.6),
                0 0 60px rgba(99, 179, 237, 0.3);
            transform: translate(-50%, -50%);
            z-index: 16;
            display: none;
            animation: pulse 2s infinite;
        }

        @keyframes pulse {
            0%, 100% {
                transform: translate(-50%, -50%) scale(1);
                opacity: 1;
                box-shadow: 
                    0 0 20px rgba(99, 179, 237, 1),
                    0 0 40px rgba(99, 179, 237, 0.6),
                    0 0 60px rgba(99, 179, 237, 0.3);
            }
            50% {
                transform: translate(-50%, -50%) scale(1.3);
                opacity: 0.9;
                box-shadow: 
                    0 0 30px rgba(99, 179, 237, 1.2),
                    0 0 60px rgba(99, 179, 237, 0.8),
                    0 0 90px rgba(99, 179, 237, 0.5);
            }
        }

        .highlight-point.show {
            display: block;
        }
    </style>
</head>
<body>
    <div id="canvas-container">
        <div id="loading">
            <div class="loading-spinner"></div>
            <div>正在加载 3D 模型...</div>
        </div>
        
        <!-- 标题区域 -->
        <div id="title-section" class="hidden">
            <h1>
                <span class="icon">⚙️</span>
                活塞各部位名称及简介
            </h1>
            <div class="subtitle">NAMES AND INTRODUCTIONS OF VARIOUS PARTS OF THE PISTON</div>
        </div>

        <!-- 连接线canvas -->
        <canvas id="connection-line"></canvas>

        <!-- 高亮点 -->
        <div id="highlight-point" class="highlight-point"></div>

        <!-- 右侧说明面板 -->
        <div id="description-panel">
            <h2 id="part-title">部位名称</h2>
            <p id="part-description">点击模型部位查看详细信息</p>
        </div>

        <!-- 导航按钮 -->
        <div id="navigation-bar" class="hidden">
            <div class="nav-arrow">◀◀</div>
            <div class="nav-button" data-part="piston-crown">活塞顶面</div>
            <div class="nav-button" data-part="valve-relief">气门避让坑</div>
            <div class="nav-button" data-part="installation-mark">装机标识</div>
            <div class="nav-button" data-part="oil-hole">油孔</div>
            <div class="nav-button" data-part="piston-skirt">活塞裙部</div>
            <div class="nav-arrow">▶▶</div>
        </div>

        <div id="info" class="hidden">
            <h3>3D 模型查看器</h3>
            <p>点击模型可以查看详细信息</p>
        </div>
        <div id="controls" class="hidden">
            <p><strong>💡 操作提示：</strong></p>
            <p>🖱️ 鼠标拖拽：旋转模型</p>
            <p>🔍 滚轮：缩放模型</p>
            <p>👆 点击：选中模型部分</p>
            <p>📱 触摸：支持触摸手势</p>
        </div>
    </div>

    <script type="importmap">
        {
            "imports": {
                "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
                "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
            }
        }
    </script>

    <script type="module">
        import * as THREE from 'three';
        import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
        import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
        import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
        import { OutlineEffect } from 'three/addons/effects/OutlineEffect.js';

        // 场景设置
        const scene = new THREE.Scene();
        scene.background = null; // 透明背景，显示页面背景

        // 相机设置
        const camera = new THREE.PerspectiveCamera(
            75,
            window.innerWidth / window.innerHeight,
            0.1,
            1000
        );
        camera.position.set(0, 0, 5);

        // 渲染器设置
        const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        document.getElementById('canvas-container').appendChild(renderer.domElement);

        // 轨道控制器（用于拖拽和缩放）
        const controls = new OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;
        controls.minDistance = 1;
        controls.maxDistance = 20;
        controls.enablePan = true;

        // 灯光设置
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
        scene.add(ambientLight);

        const directionalLight1 = new THREE.DirectionalLight(0xffffff, 0.8);
        directionalLight1.position.set(5, 5, 5);
        directionalLight1.castShadow = true;
        scene.add(directionalLight1);

        const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.4);
        directionalLight2.position.set(-5, -5, -5);
        scene.add(directionalLight2);

        // 添加点光源增强效果
        const pointLight = new THREE.PointLight(0xffffff, 0.5);
        pointLight.position.set(0, 0, 10);
        scene.add(pointLight);

        // 鼠标和射线检测
        const raycaster = new THREE.Raycaster();
        const mouse = new THREE.Vector2();
        let selectedObject = null;
        const originalMaterials = new Map();

        // 连接线canvas
        const lineCanvas = document.getElementById('connection-line');
        const lineCtx = lineCanvas.getContext('2d');
        lineCanvas.width = window.innerWidth;
        lineCanvas.height = window.innerHeight;

        // 当前选中的部位信息
        let currentPartInfo = null;
        let highlightPoint = document.getElementById('highlight-point');
        let descriptionPanel = document.getElementById('description-panel');

        // 部位说明文案
        const partDescriptions = {
            'piston-crown': {
                name: '活塞顶面',
                description: '活塞顶面是燃烧室重要组成部分，直接承受燃气燃烧产生的高温高压，其形状需精准契合发动机燃烧室结构，以保障燃烧效率与动力输出。'
            },
            'valve-relief': {
                name: '气门避让坑',
                description: '气门避让坑是活塞顶部为气门运动预留的空间，防止气门与活塞发生碰撞，确保发动机正常运转，其深度和形状需根据气门运动轨迹精确设计。'
            },
            'installation-mark': {
                name: '装机标识',
                description: '装机标识用于指示活塞的正确安装方向，确保活塞在气缸中的正确位置，避免因安装错误导致的发动机故障，是装配过程中的重要参考标记。'
            },
            'oil-hole': {
                name: '油孔',
                description: '油孔用于润滑油的流通，确保活塞与气缸壁之间的润滑效果，减少摩擦磨损，提高发动机效率和使用寿命，其位置和尺寸需精确设计。'
            },
            'piston-skirt': {
                name: '活塞裙部',
                description: '活塞裙部是活塞的主要支撑部分，与气缸壁直接接触，承受侧向力，其表面处理和尺寸精度直接影响发动机的运行平稳性和密封性能。'
            }
        };

        // 加载 GLTF 模型
        const loader = new GLTFLoader();
        let model = null;

        loader.load(
            './无人机活塞.gltf',
            function(gltf) {
                model = gltf.scene;
                
                // 计算模型边界框并居中
                const box = new THREE.Box3().setFromObject(model);
                const center = box.getCenter(new THREE.Vector3());
                const size = box.getSize(new THREE.Vector3());
                
                // 居中模型
                model.position.x += (model.position.x - center.x);
                model.position.y += (model.position.y - center.y);
                model.position.z += (model.position.z - center.z);
                
                // 根据模型大小调整相机位置
                const maxDim = Math.max(size.x, size.y, size.z);
                const fov = camera.fov * (Math.PI / 180);
                let cameraZ = Math.abs(maxDim / 2 / Math.tan(fov / 2));
                cameraZ *= 0.5; // 更近的距离，让模型显示更大
                camera.position.set(cameraZ, cameraZ, cameraZ);
                camera.lookAt(0, 0, 0);
                
                // 给模型添加缩放，让显示更大
                model.scale.set(5, 5, 5);
                
                // 启用所有网格的交互
                model.traverse((child) => {
                    if (child.isMesh) {
                        child.castShadow = true;
                        child.receiveShadow = true;
                        // 保存原始材质
                        originalMaterials.set(child, child.material);
                    }
                });

                scene.add(model);
                
                // 隐藏加载提示，显示控制说明
                document.getElementById('loading').classList.add('hidden');
                document.getElementById('controls').classList.add('hidden');
                document.getElementById('info').classList.add('hidden');
                document.getElementById('title-section').classList.remove('hidden');
                document.getElementById('navigation-bar').classList.remove('hidden');
            },
            function(xhr) {
                const percentComplete = (xhr.loaded / xhr.total) * 100;
                console.log('加载进度: ' + percentComplete.toFixed(2) + '%');
            },
            function(error) {
                console.error('加载模型出错:', error);
                document.getElementById('loading').innerHTML = 
                    '<div style="color: #ff6b6b;">❌ 加载失败，请检查文件路径</div>';
            }
        );

        // 绘制连接线
        function drawConnectionLine() {
            if (!currentPartInfo) {
                lineCtx.clearRect(0, 0, lineCanvas.width, lineCanvas.height);
                highlightPoint.classList.remove('show');
                return;
            }

            const { worldPosition } = currentPartInfo;
            
            // 将3D世界坐标转换为屏幕坐标
            const vector = new THREE.Vector3();
            vector.copy(worldPosition);
            vector.project(camera);
            
            const x = (vector.x * 0.5 + 0.5) * window.innerWidth;
            const y = (vector.y * -0.5 + 0.5) * window.innerHeight;

            // 右侧面板位置
            const panelRect = descriptionPanel.getBoundingClientRect();
            const panelX = panelRect.left;
            const panelY = panelRect.top + panelRect.height / 2;

            // 清空画布
            lineCtx.clearRect(0, 0, lineCanvas.width, lineCanvas.height);

            // 绘制连接线 - 使用直角转折的直线段（类似L型）
            // 第一段：从模型上的点水平向右延伸一定距离
            const horizontalDistance = Math.min(150, (panelX - x) / 2); // 水平延伸距离，最多150px或到中间点
            const turnX = x + horizontalDistance; // 转折点的X坐标
            
            // 第二段：从转折点垂直向下/向上到面板高度
            // 第三段：从转折点水平延伸到面板
            
            lineCtx.strokeStyle = 'rgba(99, 179, 237, 0.9)'; // 浅蓝色，更符合科技感
            lineCtx.lineWidth = 2;
            lineCtx.shadowColor = 'rgba(99, 179, 237, 0.5)';
            lineCtx.shadowBlur = 4;
            lineCtx.beginPath();
            
            // 第一段：水平向右
            lineCtx.moveTo(x, y);
            lineCtx.lineTo(turnX, y);
            
            // 第二段：垂直方向到面板高度
            lineCtx.lineTo(turnX, panelY);
            
            // 第三段：水平延伸到面板
            lineCtx.lineTo(panelX, panelY);
            
            lineCtx.stroke();

            // 显示高亮点
            highlightPoint.style.left = x + 'px';
            highlightPoint.style.top = y + 'px';
            highlightPoint.classList.add('show');
        }

        // 显示部位说明
        function showPartDescription(partKey) {
            const partInfo = partDescriptions[partKey];
            if (!partInfo) return;

            // 恢复之前选中的物体
            if (selectedObject) {
                const originalMaterial = originalMaterials.get(selectedObject);
                if (originalMaterial) {
                    selectedObject.material = originalMaterial;
                }
                selectedObject = null;
            }

            currentPartInfo = {
                partKey: partKey,
                worldPosition: new THREE.Vector3(0, 0, 0)
            };

            // 部位名称映射（用于匹配模型中的网格名称）
            const partNameMap = {
                'piston-crown': ['crown', 'top', '顶', '顶面', 'piston', '活塞'],
                'valve-relief': ['valve', 'relief', '避让', '气门', 'valve-relief'],
                'installation-mark': ['mark', '标识', '安装', 'installation', 'mark'],
                'oil-hole': ['oil', 'hole', '孔', '油', '油孔', 'oil-hole'],
                'piston-skirt': ['skirt', '裙', '裙部', 'skirt', 'piston-skirt']
            };

            // 计算模型包围盒和中心点
            model.updateMatrixWorld(true);
            const modelBox = new THREE.Box3().setFromObject(model);
            const modelCenter = new THREE.Vector3();
            modelBox.getCenter(modelCenter);
            const modelSize = modelBox.getSize(new THREE.Vector3());

            // 尝试找到对应的网格对象来获取世界位置
            let foundMesh = null;
            let bestMatch = null;
            let maxMatchScore = 0;

            if (model) {
                const searchTerms = partNameMap[partKey] || [partKey.replace('-', '')];
                
                model.traverse((child) => {
                    if (child.isMesh) {
                        const meshName = child.name.toLowerCase();
                        let matchScore = 0;
                        
                        // 计算名称匹配分数
                        searchTerms.forEach(term => {
                            if (meshName.includes(term.toLowerCase())) {
                                matchScore += term.length;
                            }
                        });
                        
                        // 如果完全匹配，优先选择
                        if (meshName === partKey.replace('-', '') || 
                            meshName === partKey) {
                            matchScore += 100;
                        }
                        
                        // 计算位置匹配分数（根据网格位置判断）
                        const meshBox = new THREE.Box3().setFromObject(child);
                        const meshCenter = new THREE.Vector3();
                        meshBox.getCenter(meshCenter);
                        child.localToWorld(meshCenter);
                        
                        const relativePos = meshCenter.clone().sub(modelCenter);
                        const normalizedY = relativePos.y / (modelSize.y / 2);
                        const normalizedX = relativePos.x / (modelSize.x / 2);
                        
                        // 根据部位类型和位置增加分数
                        if (partKey === 'piston-crown' && normalizedY > 0.3) {
                            matchScore += 50; // 顶面在模型上部
                        } else if (partKey === 'piston-skirt' && normalizedY < -0.3) {
                            matchScore += 50; // 裙部在模型下部
                        } else if (partKey === 'valve-relief' && normalizedY > 0.1 && normalizedY < 0.5) {
                            matchScore += 30; // 气门避让坑在顶部区域
                        } else if (partKey === 'oil-hole' && Math.abs(normalizedY) < 0.5 && Math.abs(normalizedX) > 0.3) {
                            matchScore += 30; // 油孔在侧面
                        } else if (partKey === 'installation-mark' && Math.abs(normalizedY) < 0.3) {
                            matchScore += 30; // 装机标识在中间区域
                        }
                        
                        if (matchScore > maxMatchScore) {
                            maxMatchScore = matchScore;
                            bestMatch = child;
                        }
                    }
                });

                // 如果找到了匹配的网格，使用它
                if (bestMatch && maxMatchScore > 0) {
                    foundMesh = bestMatch;
                    foundMesh.updateMatrixWorld(true);
                    
                    // 计算网格的中心位置（使用包围盒）
                    const box = new THREE.Box3().setFromObject(foundMesh);
                    const center = new THREE.Vector3();
                    box.getCenter(center);
                    
                    // 如果网格有父对象，需要转换到世界坐标
                    foundMesh.localToWorld(center);
                    currentPartInfo.worldPosition = center;
                    
                    // 高亮该网格
                    selectedObject = foundMesh;
                    const highlightMaterial = new THREE.MeshStandardMaterial({
                        color: 0x63b3ed, // 浅蓝色，更符合科技感
                        emissive: 0x63b3ed,
                        emissiveIntensity: 0.5,
                        metalness: 0.8,
                        roughness: 0.2
                    });
                    if (!originalMaterials.has(foundMesh)) {
                        originalMaterials.set(foundMesh, foundMesh.material);
                    }
                    foundMesh.material = highlightMaterial;
                } else {
                    // 如果没有找到匹配，尝试使用模型中心或第一个网格
                    model.updateMatrixWorld(true);
                    const center = new THREE.Vector3();
                    let firstMesh = null;
                    model.traverse((child) => {
                        if (child.isMesh && !firstMesh) {
                            firstMesh = child;
                            const box = new THREE.Box3().setFromObject(child);
                            box.getCenter(center);
                            child.localToWorld(center);
                            currentPartInfo.worldPosition = center;
                            
                            // 高亮该网格
                            selectedObject = child;
                            const highlightMaterial = new THREE.MeshStandardMaterial({
                                color: 0x63b3ed,
                                emissive: 0x63b3ed,
                                emissiveIntensity: 0.5,
                                metalness: 0.8,
                                roughness: 0.2
                            });
                            if (!originalMaterials.has(child)) {
                                originalMaterials.set(child, child.material);
                            }
                            child.material = highlightMaterial;
                        }
                    });
                    
                    if (!firstMesh) {
                        // 如果连第一个网格都没找到，使用模型中心
                        const box = new THREE.Box3().setFromObject(model);
                        box.getCenter(center);
                        model.localToWorld(center);
                        currentPartInfo.worldPosition = center;
                    }
                }
            }
            
            // 如果还是没有找到，使用模型中心
            if (!foundMesh && model) {
                model.updateMatrixWorld(true);
                const box = new THREE.Box3().setFromObject(model);
                const center = new THREE.Vector3();
                box.getCenter(center);
                model.localToWorld(center);
                currentPartInfo.worldPosition = center;
            }

            // 更新说明面板
            document.getElementById('part-title').textContent = partInfo.name;
            document.getElementById('part-description').textContent = partInfo.description;
            descriptionPanel.classList.add('show');

            // 更新导航按钮状态
            document.querySelectorAll('.nav-button').forEach(btn => {
                btn.classList.remove('active');
                if (btn.dataset.part === partKey) {
                    btn.classList.add('active');
                }
            });

            // 绘制连接线
            drawConnectionLine();
        }

        // 鼠标点击事件
        function onMouseClick(event) {
            // 计算鼠标位置（归一化到 -1 到 1）
            mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
            mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

            // 更新射线
            raycaster.setFromCamera(mouse, camera);

            // 检测相交的物体
            const intersects = raycaster.intersectObjects(scene.children, true);

            // 处理新的选中
            if (intersects.length > 0) {
                const clickedObject = intersects[0].object;
                
                // 找到最顶层的网格对象
                let mesh = clickedObject;
                while (mesh && !mesh.isMesh) {
                    mesh = mesh.parent;
                }
                
                if (mesh && mesh.isMesh) {
                    // 恢复之前选中的物体
                    if (selectedObject && selectedObject !== mesh) {
                        const originalMaterial = originalMaterials.get(selectedObject);
                        if (originalMaterial) {
                            selectedObject.material = originalMaterial;
                        }
                    }
                    
                    selectedObject = mesh;
                    
                    // 创建高亮材质
                    const highlightMaterial = new THREE.MeshStandardMaterial({
                        color: 0x3b82f6,
                        emissive: 0x3b82f6,
                        emissiveIntensity: 0.3
                    });
                    
                    // 保存原始材质并应用高亮
                    if (!originalMaterials.has(mesh)) {
                        originalMaterials.set(mesh, mesh.material);
                    }
                    mesh.material = highlightMaterial;

                    // 更新当前部位信息：使用鼠标点击的交点位置
                    // intersects[0].point 是射线与模型表面的交点（世界坐标）
                    const clickPoint = intersects[0].point.clone();
                    
                    // 计算模型包围盒和中心点
                    model.updateMatrixWorld(true);
                    const modelBox = new THREE.Box3().setFromObject(model);
                    const modelCenter = new THREE.Vector3();
                    modelBox.getCenter(modelCenter);
                    const modelSize = modelBox.getSize(new THREE.Vector3());
                    
                    // 计算点击点相对于模型中心的位置（归一化）
                    const relativePos = clickPoint.clone().sub(modelCenter);
                    const normalizedY = relativePos.y / (modelSize.y / 2); // Y轴归一化，-1到1
                    const normalizedX = relativePos.x / (modelSize.x / 2); // X轴归一化
                    const normalizedZ = relativePos.z / (modelSize.z / 2); // Z轴归一化
                    
                    // 根据点击的网格名称和位置识别对应的部位
                    const partNameMap = {
                        'piston-crown': ['crown', 'top', '顶', '顶面', 'piston', '活塞'],
                        'valve-relief': ['valve', 'relief', '避让', '气门', 'valve-relief'],
                        'installation-mark': ['mark', '标识', '安装', 'installation', 'mark'],
                        'oil-hole': ['oil', 'hole', '孔', '油', '油孔', 'oil-hole'],
                        'piston-skirt': ['skirt', '裙', '裙部', 'skirt', 'piston-skirt']
                    };
                    
                    // 根据网格名称查找对应的 partKey
                    let matchedPartKey = null;
                    const meshName = mesh.name.toLowerCase();
                    let maxMatchScore = 0;
                    
                    // 遍历所有部位，找到最佳匹配（考虑名称和位置）
                    for (const [partKey, searchTerms] of Object.entries(partNameMap)) {
                        let matchScore = 0;
                        
                        // 名称匹配分数
                        searchTerms.forEach(term => {
                            if (meshName.includes(term.toLowerCase())) {
                                matchScore += term.length;
                            }
                        });
                        
                        // 如果完全匹配，优先选择
                        if (meshName === partKey.replace('-', '') || meshName === partKey) {
                            matchScore += 100;
                        }
                        
                        // 位置匹配分数（根据活塞结构特点）
                        if (partKey === 'piston-crown' && normalizedY > 0.3) {
                            matchScore += 50; // 顶面在模型上部
                        } else if (partKey === 'piston-skirt' && normalizedY < -0.3) {
                            matchScore += 50; // 裙部在模型下部
                        } else if (partKey === 'valve-relief' && normalizedY > 0.1 && normalizedY < 0.5) {
                            matchScore += 30; // 气门避让坑在顶部区域
                        } else if (partKey === 'oil-hole' && Math.abs(normalizedY) < 0.5 && Math.abs(normalizedX) > 0.3) {
                            matchScore += 30; // 油孔在侧面
                        } else if (partKey === 'installation-mark' && Math.abs(normalizedY) < 0.3) {
                            matchScore += 30; // 装机标识在中间区域
                        }
                        
                        if (matchScore > maxMatchScore) {
                            maxMatchScore = matchScore;
                            matchedPartKey = partKey;
                        }
                    }
                    
                    // 如果没有找到匹配，根据位置判断默认部位
                    if (!matchedPartKey || maxMatchScore === 0) {
                        if (normalizedY > 0.4) {
                            matchedPartKey = 'piston-crown'; // 上部 -> 顶面
                        } else if (normalizedY < -0.4) {
                            matchedPartKey = 'piston-skirt'; // 下部 -> 裙部
                        } else if (normalizedY > 0.1) {
                            matchedPartKey = 'valve-relief'; // 中上部 -> 气门避让坑
                        } else if (Math.abs(normalizedX) > 0.4) {
                            matchedPartKey = 'oil-hole'; // 侧面 -> 油孔
                        } else {
                            matchedPartKey = 'installation-mark'; // 其他 -> 装机标识
                        }
                    }
                    
                    currentPartInfo = {
                        partKey: matchedPartKey,
                        worldPosition: clickPoint
                    };

                    // 显示说明面板（使用匹配到的部位）
                    const partInfo = partDescriptions[matchedPartKey];
                    if (partInfo) {
                        document.getElementById('part-title').textContent = partInfo.name;
                        document.getElementById('part-description').textContent = partInfo.description;
                        
                        // 更新导航按钮状态
                        document.querySelectorAll('.nav-button').forEach(btn => {
                            btn.classList.remove('active');
                            if (btn.dataset.part === matchedPartKey) {
                                btn.classList.add('active');
                            }
                        });
                    }
                    
                    descriptionPanel.classList.add('show');

                    // 绘制连接线
                    drawConnectionLine();
                }
            } else {
                // 点击空白处，隐藏说明和连接线
                currentPartInfo = null;
                descriptionPanel.classList.remove('show');
                drawConnectionLine();
                
                // 清除导航按钮的激活状态
                document.querySelectorAll('.nav-button').forEach(btn => {
                    btn.classList.remove('active');
                });
                
                // 恢复之前选中的物体
                if (selectedObject) {
                    const originalMaterial = originalMaterials.get(selectedObject);
                    if (originalMaterial) {
                        selectedObject.material = originalMaterial;
                    }
                    selectedObject = null;
                }
            }
        }

        // 添加事件监听
        window.addEventListener('click', onMouseClick);
        window.addEventListener('touchstart', (event) => {
            if (event.touches.length === 1) {
                event.preventDefault();
                onMouseClick(event.touches[0]);
            }
        }, { passive: false });

        // 窗口大小调整
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
            lineCanvas.width = window.innerWidth;
            lineCanvas.height = window.innerHeight;
            drawConnectionLine();
        });

        // 导航按钮点击事件
        document.querySelectorAll('.nav-button').forEach(button => {
            button.addEventListener('click', () => {
                const partKey = button.dataset.part;
                showPartDescription(partKey);
            });
        });

        // 动画循环
        function animate() {
            requestAnimationFrame(animate);
            
            // 更新控制器
            controls.update();
            
            // 如果模型存在，可以添加自动旋转（可选）
            // if (model) {
            //     model.rotation.y += 0.005;
            // }
            
            // 更新连接线（起点保持固定在点击位置，只更新终点）
            if (currentPartInfo && selectedObject) {
                // 起点位置保持不变（固定在鼠标点击的位置）
                // 只需要重新绘制连接线，因为面板位置可能会变化
                drawConnectionLine();
            }
            
            renderer.render(scene, camera);
        }

        animate();
    </script>
</body>
</html>

